package Controllers;

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lejos.pc.comm.NXTConnectionState;
import lejos.pc.comm.NXTInfo;
import BLL.Model;
import GUI.ConnectionFrame;
import GUI.ConnectionThread;
import GUI.MapperMain;

/**
 * Main <b>Controller</b> class for the MVC pattern.
 * Handles threads for new GUI frames and is responsible for
 * functionality between the GUI and the model in a MVC architecture.
 * 
 * @author Danny Johansen, Didzis Petkus, Liviu Berheci.
 * 
 */
public class Controller {

	private MapperMain view;
	private Model model;
	private ExecutorService pool;

	/**
	 * Constructor connecting the <b>model</b> and <b>view</b> to this object.
	 * 
	 * @param view
	 *            the given instance of {@link MapperMain} class.
	 * @param model
	 *            the given instance of {@link Model} class
	 * 
	 */
	public Controller(MapperMain view, Model model) {
		this.view = view;
		this.model = model;
		pool = Executors.newCachedThreadPool();

		view.assignController(new ButtonController(this));
		view.addWindowListener(new WindowController());
		view.addTableSelectionListener(new TableSelectionController(this));
	}

	/**
	 * Calls the <b>model</b> to search for any NXT devices.
	 */
	public void searchNXT() {
		try {
			// view.updateStatus("Searching...");
			model.getBluetoothDevices();
		} catch (Exception e) {
			view.showError(e);
		}
		view.enableSearch(false);
	}

	/**
	 * Calls the <b>view</b> to display an error message.
	 * 
	 * @param e - Exception to display the error message from.
	 */
	public void showError(Exception e) {
		view.showError(e);
	}

	/**
	 * Calls the <b>model</b> to stop searching.
	 */
	public void stopSearch() {
		// System.out.println("Stopping search...");
		view.updateStatus("Search stopped");
		try {
			model.stopSearch();
		} catch (Exception e) {
			view.showError(e);
		}
		view.enableSearch(true);
	}

	/**
	 * Calls the <b>view</b> to toggle the connect button
	 *  and setting the state to be enabled/disabled.
	 *  
	 * @param enabled - state to set the button.
	 */
	public void toggleConnectBtn(boolean enabled) {
		view.toggleConnectBtn(enabled);
	}

	/**
	 * Toggles between calling connect or disconnect method depending on
	 * current connection state of the NXTInfo corresponding
	 * to the selected table entry.
	 */
	public void toggleConnection() {
		NXTInfo deviceInfo = view.getSelectedNXT();

		if (deviceInfo.connectionState == NXTConnectionState.LCP_CONNECTED) {
			disconnectNXT(deviceInfo);
		} else {
			connectNXT(deviceInfo);
		}
	}

	/**
	 * Calls the <b<model</b> to connect to a device
	 * matching the passed NXTInfo. Calls the <b>view</b> to display
	 * any error messages received.
	 * <br> On success, starts a new thread for displaying a connection view.
	 * @param deviceInfo - NXTInfo matching a found NXT device.
	 */
	private void connectNXT(NXTInfo deviceInfo) {
		view.updateStatus("Connecting to - " + deviceInfo.name);
		System.out.println("Connecting to - " + deviceInfo.name);
		try {
			if (!model.connectBluetoothDevice(deviceInfo)) {
				view.updateStatus("Failed to connect to device: "
						+ deviceInfo.name);
				System.out.println("NOT CONNECTED FOR SOME GOOD REASON!");
			} else {
				System.out
						.println("Controller trying to submit a new thread (93)");
				pool.submit(new ConnectionThread(deviceInfo, view.getBounds()
						.getMaxX(), view.getBounds().getMinY(), this));
				System.out.println("Controller submitted thread (95)");
			}
		} catch (IOException e) {
			view.showError(e);
			e.printStackTrace();
		}
	}

	/**
	 * Disconnects a device matching the passed NXTInfo bean.
	 * Calls the view to display any errors caught.
	 * @param deviceInfo - NXTInfo matching a found NXT device.
	 */
	private void disconnectNXT(NXTInfo deviceInfo) {
		view.updateStatus("Disconnecting from - " + deviceInfo.name);
		try {
			model.disconnectBluetoothDevice(deviceInfo);
		} catch (IOException e) {
			view.showError(e);
			e.printStackTrace();
		}
	}

	/**
	 * Registers a passed connection view as an observer of
	 * the <b>model</b>.
	 * 
	 * @param frame - the passed {@link ConnectionFrame} object.
	 */
	public void ConnectionFrameCreated(ConnectionFrame frame) {
		model.addObserver(frame);
		frame.addMapPanelMouseListener(new ConnectionFrameListener(this, frame));
	}

}
